 
local version       = require "version"
local socket        = require "socket"
local timer         = require "timer"
local show          = require "showmsg"
local date          = require "date"
local random        = require "random"
local char_mmo      = require "mmo"
                      require "strlib"
local mmo           = require "login.login_mmo"
local loginlog      = require "login.loginlog"
local account       = require "login.account_sql"
local manager       = require "login.manager"
local sharedata     = require "skynet.sharedata"


SERVER_TYPE  =  SERVER_NONE

local login_config = Login_Config

local AccountDB

local login_fd

local msg_table = {}       -- 消息表

local CONF = {}

local allowed_regs    = 1  -- 允许注册数
local time_allowed    = 10 -- 允许秒 

local num_regs        = 0  -- 当前的注册数
local new_reg_tick    = 0  -- 时间

local mmo_char_server = {} -- 服务器信息
local auth_db         = {} 
local online_db       = {} -- 在线数据(创建管理器)
local login           = {}   

----------------------------------
--   验证数据库
----------------------------------

function login:create_auth_node()

    local auth_node = {
        local account_id    = 0,
        local login_id1     = 0,
        local login_id2     = 0,
        local ip            = 0,
        local sex           = 0,
        local version       = 0,
        local clienttype    = 0,
        }

    return auth_node 
end 

---------------------------------
-- 在线用户数据
---------------------------------
function login:create_online_data()

    local online_login_data = {

    local account_id  = 0,
    local waiting_disconnect = 0,
    local char_server = 0,
    }

    return online_login_data
end 

-- 找到该角色
function M:add_find(fd)

    if mmo_char_server[fd] and mmo_char_server[fd].fd == fd  then
        return true     
    end 

    return false 
end 

-- 登录服务器初始化
function login:do_init()
end

-- 玩家退出游戏时调用
function login:do_shutdown()

    show:status("关闭...")
    
    for k,v in pairs(mmo_char_server) do 
        self:chrif_server_destroy(k)
    end 
end 

-- 关闭服务器时善后处理
function login:do_final()

   loginlog:log(0, "login server",100, "login server shutdown")
   show:status("终止...")
   
   if login_config.log_login then  loginlog:final() end 
   account:db_sql_destroy(AccountDB)
   AccountDB = nil 
   online_db = nil 
   auth_db   = nil  

   -- 遍历服务器
   for k,v in pairs(mmo_char_server) do 
        self:chrif_server_destroy(k)
   end 

   if login_fd ~= -1 then 
    -- do_close(login_fd)
    -- login_fd = -1 
   end 
   show:status("完成.")
end 


function login:set_defaults()

    login_config.login_ip                   = ''
    login_config.login_port                 = 6090
    login_config.ip_sync_interval           = 0
    login_config.log_login                  = true 
    login_config.date_format                = "%Y-%m-%d %H:%M:%S" 
    login_config.console                    = false 
    login_config.min_level_to_connect       = 0
    login_config.check_client_version       = false 
    login_config.client_version_to_connect  = 20
    login_config.use_md5_passwds            = false 
end 

-- 设置服务器类型
function login:set_server_type()

    SERVER_TYPE = SERVER_LOGIN 
end 

-- 读取配置文件
function login:config_read(cfgName)
    
    local line

    local fp = io.open(cfgName, "r")
    
    if not fp then 

        show:error("配置文件 (%s) 未找到", cfgName)
        return 1
    end 

    show:info("读取配置文件 %s...", cfgName)

    while true do 
        
        line = fp:read("*line")
        
        if not line then 
            break
        end 

        if line ~= "" and not string.find(line, "#") then 
            
            local i = string.find(line, ":")

            if i then 
                
                local left, right
                left  = str_trim(string.sub(line, 1,i-1))
                right = str_trim(string.sub(line, i+1))
                        
                if left == "timestamp_format"                   then 
                        CONF.log_timestamp                       = right 
                elseif left == "console_silent"                 then 
                        show:info("控制台静止 %d", tonumber(right))
                        CONF.console_silent                      = tonumber(right)
                elseif left == "console_msg_log"                then
                        CONF.console_msg_log                     = tonumber(right)
                elseif left == "console_log_filepath"           then 
                        CONF.DEBUGLOGPATH                        = right 
                elseif left == "bind_ip"                        then
                        login_config.login_ip                    = right
                        if login_config.login_ip ~= ""          then 
                            show:status("登录服务器绑定IP地址: %s", login_config.login_ip)
                        end 
                elseif left == "login_port"                     then 
                        login_config.login_port                  = tonumber(right)
                        show:status("设置 login_port : %s", right)
                elseif left == "log_login"                      then 
                        login_config.log_login                   = config_switch(right)
                elseif left == "start_limited_time"             then 
                         login_config.start_limited_time         = tonumber(right)
                elseif left == "check_client_version"           then 
                         login_config.check_client_version       = config_switch(right)
                elseif left == "client_version_to_connect"      then 
                         login_config.client_version_to_connect  = tonumber(right)                
                elseif left == "use_md5_passwds"                               then 
                         login_config.use_md5_passwds            = config_switch(right)
                elseif left == "min_level_to_connect"           then  
                         login_config.min_level_to_connect       = tonumber(right)                 
                elseif left == "date_format"                    then
                         login_config.date_format                = right
                elseif left == "consolf"                        then 
                         login_config.console                    = config_switch(right)
                elseif left == "allowed_regs"                   then 
                         allowed_regs                            = tonumber(right)
                elseif left == "time_allowed"                   then          
                         time_allowed                            = tonumber(right)
                elseif left == "ip_sync_interval"               then  
                         login_config.ip_sync_interval           = tonumber(right) 
                elseif left == "import"                         then 
                         self:config_read(right) 
                else
                    account:db_sql_set_property(AccountDB, left, right, string.len(left))
                    loginlog:config_read(left, right)
                end 
            end 
        end 
    end 

    fp:close()
    show:info("完成读取　%s", cfgName)
    CONF.DEBUGLOGLOGIN = true 
    sharedata:new("CONF", CONF) -- 需要的配置数据共享 

    return 0
end 

-- 读取消息配置表
function login:msg_config_read(cfgName)

    local line
    local fp = io.open(cfgName, "r")

    if not fp then 
        show:error("配置文件 (%s) 未找到", cfgName)
        return 1
    end 

    while true do  

    end 
end 

-- 获取消息类型
function login:get_msg(index)

    if msg_table[index] then 

        return msg_table[index] 
    else 
        show:waring("不存在的消息类型 index= %d", index)
    end     
end 

-- 清除消息配置表
function login:do_final_msg()

    msg_table = nil 
end

-- 默认数据包解析（正常玩家或角色服务器连接请求）
function login:parse(fd)  
    
    local session  = manager:get(fd)
    local loginsd = session.session_data
    local ip      = session.client_addr
    local result 

    if session.flag.eof then 
        show:info("关闭 %s 连接", ip)
        -- do_close()
        return 0 
    end 

    if loginsd == 0 then

        -- 黑名单检查　未写(指未添加该功能)
        ----------------
        
        -- 创建回话数据
        session.session_data = mmo:create_session_data()
        loginsd              = session.session_data
        loginsd.fd           = fd
    end

    if not session.flag.eof and  session.rsize > 0 then 
            
        -- if session.type and session.type == "RESPONSE" then     
        -- RESPONSE　消息类型处理
        -- end 
        if session.name == "login" then 
            
            do
                local version 
                local username 
                local password 
                local clienttype 
                local passhash
                local israwpass  = session.rdata.israwpass
                
                version    = session.rdata.version
                username   = session.rdata.user
                clienttype = session.rdata.clienttype 

                if israwpass then 

                    password = session.rdata.pawd    
                else 
                    passhash = session.rdata.pawd
                end 
                
                loginsd.clienttype = clienttype
                loginsd.version    = version
                loginsd.userid     = username
                
                if israwpass then 

                    show:status("请求连接的 %s (ip: %s)", loginsd.userid, ip)
                    loginsd.passwd    = password
                    if login_config.use_md5_passwds then 
                        -- MD5_String(loginsd.passwd, loginsd.passwd) -- 数据库的加密手段
                    end 
                    loginsd.passwdenc = 0
                else 
                    show:status("请求连接 %s (加密模式) (ip: %s) ", loginsd.userid ,ip)
                    -- bin2hex() (原始的二进制数据在这里)
                    loginsd.passwdenc = PASSWORDENC
                    
                end 

                if loginsd.passwdenc ~= 0 and login_config.use_md5_passwds then  
                    self:auth_failed(loginsd, 3) -- 发送“拒绝从服务器”
                    return 0
                end 

                result = self:mmo_auth(loginsd)
                
                if result == -1 then 

                     self:login_auth_ok(loginsd)
                else 
                     self:auth_failed(loginsd, result)
                end 
                
            end 

        elseif session.name == "server" then 

                local server_name 
                local message 
                local server_ip 
                local server_port 
                local type 
                local new_ 


                loginsd.userid = session.rdata.user
                loginsd.passwd = session.rdata.pawd

                if login_config.use_md5_passwds then 
                    -- MD5_String(loginsd.passwd, loginsd.passwd) -- 数据库的加密手段s
                end 
 
                loginsd.passwdenc   = 0
                loginsd.version     = session.rdata.version
                server_ip           = session.rdata.ip
                server_port         = session.rdata.port
                server_name         = session.rdata.name
                type                = session.rdata.type
                new_                = session.rdata.new

                show:info("角色服务器的连接请求 %s (账户: %s, 密码: %s, ip: %s)",server_name, loginsd.userid, loginsd.passwd ) 
                message = string.format("char-server %s %s:d", server_name, server_ip, server_port)
                loginlog:log(session.client_addr, loginsd.userid, 100, message)
                
                result = self:mmo_auth(loginsd)
                
                if result == -1 and loginsd.sex == 'S' and loginsd.account_id >= 0 and  loginsd.account_id < MAX_SERVERS then 

                   show:status("连接的角色服务器 %s 接受连接", server_name)
                   
                   local SERVER     = mmo:create_server()
                   SERVER.name      = server_name
                   SERVER.fd        = fd
                   SERVER.ip        = server_ip
                   SERVER.port      = server_port
                   SERVER.users     = 0
                   SERVER.type      = type 
                   SERVER.new_      = new 

                   session.func_parse   = self.parse_fromchar
                   session.flag.server  = true -- 将标志设置成　true = 服务器  false = 用户 
                   
                   mmo_char_server[loginsd.account_id] = SERVER -- 保存到表中
                   
                   --
                   -- sending server 将结果发送给服务器 
                   --
                else 
                    show:notice("连接的角色服务器 %s 拒绝连接", server_name)
                    --
                    -- 返回结果
                    --
                end 
                
                return 0 -- 将继续处理其他的
        else 
            show:notice("连接异常结束 (ip: %s) 未知包 %s", ip, session.name)
            session.flag.eof = true 
        end 
        
    end 

end 

-- 登录认证失败
function login:auth_failed(sd, result)

    local fd = sd.fd 
    --local ip = manager:get(fd).client_addr
    if login_config.log_login then 

        loginlog:log(ip or "", sd.userid, result, msg_table[result] or "Unknow Error" )
    end

    --
    -- 将结果写入缓冲区中 require("result") 暂未写
    --

    if result ~= 6 then 

    else 
         local acc        = mmo:create_mmo_account()
         local unban_time = (account:db_sql_load_str(AccountDB,acc, sd.userid) and acc.unban_time) or 0
         local timestring = date:timestamp2string(unban_time, login_config.date_format)
    end  

    -- 发送client 未写   
end 

-- 登录成功 sd = login_session_data 
function login:auth_ok(sd)
    
    local fd = sd.fd 
    local ip = manager:get(fd).client_addr    
    local server_num = 0
    local node

                                --  无限制 -1
    if sd.level <  login_config.min_level_to_connect then 

        show:status("连接拒绝: 连接的最小GM等级是 %d (账户: %s GM等级: %d)", login_config.min_level_to_connect, sd.userid, sd.level)
        --
        --  写入buf中返回　client 
        --  01  = server closed 
        return 
    end 

    -- 检查是否开启角色服务器
    for k,v in pairs(mmo_char_server) do 
        
        if k and v.fd  then
            server_num = server_num +1
        end  
    end 

    -- 无角色服务器开则拒绝连接
    if server_num == 0 then 

        show:status("拒绝连接: 没有在线的角色服务器　(account: %s)",sd.userid)
        --
        --  写入buf中返回　client 
        --  01  = server closed   
        return 

    end 

    do 
        local onlinedata = online_db:get(sd.account_id)
        
        if onlinedata then 

            if onlinedata.char_server > -1 then 
                show:notice("用户 %s 已经在线 -- 拒绝", sd.userid)
                --
                -- buf 
                --
                --　self:charif_sendallwos(-1, buf)

                if onlinedata.waiting_disconnect == INVALID_TIMER then 
                    -- timer:add() 添加定时器
                end 

                return 

            else 

                if onlinedata.char_server == -1 then 
                    auth_db:remove(sd.account_id)
                    self:remove_online_user(sd.account_id)
                    onlinedata = nil 
                end 
            end 
        end 
    end 

    loginlog:log(ip or "", sd.userid, 100, "login ok")
    
    if sd.level > 0 then 
        show:status("GM (等级: %d) 账户 %s 的连接已被接受", sd.level, sd.userid)
    else 
        show:status("已接受账户 %s 的连接:", sd.userid)
    end 


    for k,v in pairs(mmo_char_server) do 
        --
        --  mmo_char_server.user
        --  mmo_char_server.type               
        --  mmo_char_server.new_ 写入buf 发送 client
    end 

    -- 检查发送client

    --创建临时身份验证入口
    node = self:create_auth_node()
    node.account_id = sd.account_id
    node.login_id1  = sd.login_id1
    node.login_id2  = sd.login_id2
    node.sex        = sd.sex
    node.ip         = ip or ""
    node.version    = sd.version
    node.clienttype = sd.clienttype
    
    -- 放入管理器中
    auth_db:add(sd.account_id, node)

    do 
        -- 将客户端标记为在线
        local online_login_data = self:add_online_user(-1, sd.account_id)

        -- online_login_data.waiting_disconnect = timer:add()
    end 
end 

--[[
    make_listen_bind() -- 创建 socket_data  并初始化　ip

]]--

 -- new账号
function login:mmo_auth_new(userid, pass, sex, last_ip)

    --[[
    -- num_regs = 0 
    -- new_reg_tick = 0

    local tick = os.time()

    if new_reg_tick == 0 then 
        new_reg_tick = os.time()
    end 

    if tick - new_reg_tick < 0 and num_regs >=  allowed_regs then 

        show:notice("帐户注册被拒绝(超过注册上限)")
        return 3
    end 
    ]]--

    if sex ~= 'M' and sex ~= 'F' then

        return 0
    end

    local acc = mmo:create_mmo_account()

    local result  = account:db_sql_load_str(AccountDB, acc, userid)

    if result == true then 

        show:notice("尝试创建已存在的账号 (账号: %s_%c, 密码: %s, 收到密码: %s)", userid, sex, acc.user_pass, pass)
        return 1
    end 

    acc.account_id      = -1 
    acc.userid          = userid
    acc.user_pass       = pass 
    acc.sex             = sex 
    acc.email           = "a@a.com"
    acc.expiration_time = (login_config.start_limited_time ~= -1  and date:get_timestamp() + login_config.start_limited_time) or 0
    acc.lastlogin       = "0000-00-00 00:00:00"
    acc.last_ip         = last_ip
    acc.birthdate       = "0000-00-00"

    if not account:db_sql_create(AccountDB, acc) then 

        return 0 
    end 

    show:notice("账户创建 (account %s, id: %d, pass: %s, sex: %c)", acc.userid, acc.account_id, acc.user_pass, acc.sex)

    --[[ 
    if tick - new_reg_tick > 0 then 
        num_regs = 0;
        new_reg_tick = tick + time_allowed
    end

    num_regs = num_regs + 1
    ]]--

    return -1
end 


--检查连接认证            sd  = login_session_data
function login:mmo_auth(sd)

    local acc = mmo:create_mmo_account()
    local ip  = manager:get(sd.fd).client_addr
    
    -- 未实现 DNS黑名单检查

    -- 检查客户端本版
    if login_config.check_client_version  and sd.version ~= login_config.client_version_to_connect then 
    
        return 5
    end 

    -- 未实现　账户创建 _M/_F(未实现指未写功能 其他功能一切正常) 

    -- 加载用户数据
    if not account:db_sql_load_str(AccountDB, acc, sd.userid) then 

        show:notice("未知账户 (账号: %s, 收到密码: %s, ip: %s)", sd.userid, sd.passwd, ip or "")
        return 0 -- 0 = 未知ID
    end

    -- 检查的密码
    if not self:check_password(0, sd.passwdenc, sd.passwd, acc.user_pass) then 
        
        show:notice("无效密码　(账户: %s, 密码: %s, 接收密码: %s, IP: %s)",sd.userid, acc.user_pass, sd.passwd, ip or "")
        return 1 -- 无效密码
    end 

    -- 检查账号是否有效时间
    if acc.expiration_time ~= 0 and acc.expiration_time < date:get_timestamp() then 
        
        show:notice("拒绝连接 (账户: %s, 密码: %s, 无效 ID, IP: %s)", sd.userid, sd.passwd, ip or "")
        return 2 -- 无效ID
    end 

    -- 禁止时间
    if acc.unban_time ~= 0 and acc.unban_time < date:get_timestamp() then 

        local tmpstr = date:timestamp2string(acc.unban_time, login_config.date_format)
        show:notice("拒绝连接 (账户: %s, 密码: %s, 禁止直到 %s, IP: %s)", sd.userid, sd.passwd, tmpstr, ip or "" )
        return 6 -- 禁止你登录到 %s
    end 

    -- 账户状态
    if acc.state ~= 0 then 
        
        show:notice("拒绝连接 (账户: %s, 密码: %s, 状态: %d, IP: %s)", sd.userid, sd.passwd, acc.state, ip or "" )
        return acc.state -1 
    end

    show:notice("认证接收 (账户: %s, ID: %d, ip: %s)", sd.userid, acc.account_id, ip or "") 
    
    -- 保存相关数值
    sd.account_id   = acc.account_id
    sd.login_id1    = random:sh_rand()
    sd.login_id2    = random:sh_rand()
    sd.lastlogin    = acc.lastlogin
    sd.sex          = acc.sex
    sd.level        = acc.level
    acc.unban_time  = 0
    acc.logincount  = acc.logincount + 1

    -- 保存登录更新数值
    account:db_sql_save(AccountDB, acc)
    
    -- 当服务器连接的则打印 这行
    if sd.sex ~= 'S' and sd.account_id <  MAX_START_ACCOUNT_NUM then 

        show:warning("账户: %s 账户id: %d, 账户id %d 必须超过 %d 才能工作", sd.userid, sd.account_id, MAX_START_ACCOUNT_NUM)
    end 

    return -1 -- account OK
end 

-- 角色服务器包解析
function login:parse_fromchar(fd)
end 

-- 控制台命令解析
function login:parse_console(command)
end

-- 加密和未加密的密码检查
function login:check_encrypted(str1, str2, passwd)
end 

-- 检查密码
function login:check_password(md5key, passwdenc, passwd, refpass)

    if passwdenc == 0 then 
        
        return passwd == refpass 
    else 
        -- 另外一种加密方式(暂未考虑实现)
    end 
end  

function charif_sendallwos(sfd, buf)
    -- 循环所有角色 
    -- 发送给角色服务器
    -- 返回发送的个数
end 

-- 定期IP地址同步
function login:sync_ip_address(tid, tick, id, data)

    show:info("IP 同步正在进行中...")
    -- local buf  未实现
    --self:charif_sendallwos(-1,buf)
    --
end 

-- 与连接角色服务器的连接断开时调用
function login:chrif_on_disconnect(id)

    show:status("角色服务器　%s 已断开连接", mmo_char_server[id].name)
    self:chrif_server_reset(id)
end 


-- 重置与服务器相关的所有数据
function login:chrif_server_reset(id)

    self:online_db_setoffline(id)
    self:chrif_server_destroy(id)
    self:chrif_server_init(id)
end 

--[[
-- 定制时间戳
function login:timestamp_string(format)
    
    return date:get_current_time(format)
end 
]]--

-- 初始化服务器结构
function login:chrif_server_init(id)

    -- id 为角色的下标
    if mmo_char_server[id] then     
        mmo_char_server[id].fd = nil     
    end 
end 

function login:chrif_server_destroy(id)

    if mmo_char_serve[id] and mmo_char_serve[id] ~= -1 then 
        -- 
        -- do_close(id)
        mmo_char_serve[id].fd = -1 
    end 
end 

function login:online_data_cleanup_sub(key, data, ap)
end 

-- 在线数据清理
function login:online_data_cleanup()
end 


-- 在线数据库设置成离线(角色保存的服务器addr)
function login:online_db_setoffline(id)

    local onlinedata = online_db:get(id)

    if server == -1 then 
        
        onlinedata.char_server = -1 

        if onlinedata.waiting_disconnect ~=  INVALID_TIMER then 
            timer:remove(onlinedata.waiting_disconnect) 
            onlinedata.waiting_disconnect = INVALID_TIMER
        end 
    elseif onlinedata.char_server == server then 

        onlinedata.char_server = -2 
    end 

    return false 
end 

-- 定时器断开
function login:waiting_disconnect_timer(tid, id)

    local onlinedata
    
    onlinedata = online_db:get(id)    

    if onlinedata ~= nil and onlinedata.waiting_disconnect == tid and onlinedata.account_id == id then 
        
        onlinedata.waiting_disconnect = INVALID_TIMER
        self:remove_online_user(id)
        auth_db:remove(id)
    end 

    return false
end 

-- 移除指定的在线用户
function login:remove_online_user(account_id)

    local onlinedata

    onlinedata = online_db:get(account_id)

    if not onlinedata then return end 

    if onlinedata.waiting_disconnect ~=  INVALID_TIMER then 

        timer:remove(onlinedata.waiting_disconnect) 
        onlinedata.waiting_disconnect = INVALID_TIMER
    end 

    -- 在线管理中移除
    online_db:remove(account_id)
end 

-- 在线添加用户
function login:add_online_user(char_server, account_id)
    
    local onlinedata

    onlinedata = self:create_online_user(account_id)
    onlinedata.char_server = char_server

    if onlinedata.waiting_disconnect ~=  INVALID_TIMER then 

        timer:remove(onlinedata.waiting_disconnect) 
        onlinedata.waiting_disconnect = INVALID_TIMER
    end 

    -- 添加到在线管理器
    online_db:add(account_id, onlinedata)

    return onlinedata
end 


-- 在线创建用户
function login:create_online_user(key)

    local onlinedata

    onlinedata = self:create_online_data()
    onlinedata.account_id = key
    onlinedata.char_server = -1
    onlinedata.waiting_disconnect = INVALID_TIMER

    return onlinedata
end 

return login





 





